package com.xiaochao.lcrapiddeveloplibrary.Exception.core;

import android.content.Intent;
import android.text.TextUtils;

import com.xiaochao.lcrapiddeveloplibrary.Exception.callback.RecoveryCallback;
import com.xiaochao.lcrapiddeveloplibrary.Exception.tools.DefaultHandlerUtil;
import com.xiaochao.lcrapiddeveloplibrary.Exception.tools.RecoverySharedPrefsUtil;
import com.xiaochao.lcrapiddeveloplibrary.Exception.tools.RecoverySilentSharedPrefsUtil;
import com.xiaochao.lcrapiddeveloplibrary.Exception.tools.RecoveryUtil;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * Created by zhengxiaoyong on 16/8/26.
 */
final class RecoveryHandler implements Thread.UncaughtExceptionHandler {

	private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;

	private RecoveryCallback mCallback;

	private RecoveryStore.ExceptionData mExceptionData;

	private String mStackTrace;

	private String mCause;

	private RecoveryHandler(Thread.UncaughtExceptionHandler defHandler) {
		mDefaultUncaughtExceptionHandler = defHandler;
	}

	static RecoveryHandler newInstance(Thread.UncaughtExceptionHandler defHandler) {
		return new RecoveryHandler(defHandler);
	}

	@Override
	public synchronized void uncaughtException(Thread t, Throwable e) {
		e.printStackTrace( );
		if (Recovery.getInstance( ).isSilentEnabled( )) {
			RecoverySilentSharedPrefsUtil.recordCrashData( );
		} else {
			RecoverySharedPrefsUtil.recordCrashData( );
		}

		StringWriter sw = new StringWriter( );
		PrintWriter pw = new PrintWriter(sw);
		e.printStackTrace(pw);
		pw.flush( );

		String stackTrace = sw.toString( );
		String cause = e.getMessage( );
		Throwable rootTr = e;
		while (e.getCause( ) != null) {
			e = e.getCause( );
			if (e.getStackTrace( ) != null && e.getStackTrace( ).length > 0)
				rootTr = e;
			String msg = e.getMessage( );
			if (!TextUtils.isEmpty(msg))
				cause = msg;
		}

		String exceptionType = rootTr.getClass( ).getName( );

		String throwClassName;
		String throwMethodName;
		int throwLineNumber;

		if (rootTr.getStackTrace( ).length > 0) {
			StackTraceElement trace = rootTr.getStackTrace( )[0];
			throwClassName = trace.getClassName( );
			throwMethodName = trace.getMethodName( );
			throwLineNumber = trace.getLineNumber( );
		} else {
			throwClassName = "unknown";
			throwMethodName = "unknown";
			throwLineNumber = 0;
		}

		mExceptionData = RecoveryStore.ExceptionData.newInstance( )
				.type(exceptionType)
				.className(throwClassName)
				.methodName(throwMethodName)
				.lineNumber(throwLineNumber);

		mStackTrace = stackTrace;
		mCause = cause;

		if (mCallback != null) {
			mCallback.stackTrace(stackTrace);
			mCallback.cause(cause);
			mCallback.exception(exceptionType, throwClassName, throwMethodName, throwLineNumber);
			mCallback.throwable(e);
		}

		if (!DefaultHandlerUtil.isSystemDefaultUncaughtExceptionHandler(mDefaultUncaughtExceptionHandler)) {
			if (mDefaultUncaughtExceptionHandler == null)
				recover( );
			mDefaultUncaughtExceptionHandler.uncaughtException(t, e);
		} else {
			recover( );
		}
	}

	RecoveryHandler setCallback(RecoveryCallback callback) {
		mCallback = callback;
		return this;
	}

	private void recover() {
		if (RecoveryUtil.isAppInBackground(Recovery.getInstance( ).getContext( ))
				&& !Recovery.getInstance( ).isRecoverInBackground( )) {
			killProcess( );
			return;
		}
		if (Recovery.getInstance( ).isSilentEnabled( )) {
			startRecoverService( );
		} else {
			startRecoverActivity( );
		}
	}

	private void startRecoverActivity() {
		Intent intent = new Intent( );
		intent.setClass(Recovery.getInstance( ).getContext( ), RecoveryActivity.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
		if (RecoveryStore.getInstance( ).getIntent( ) != null)
			intent.putExtra(RecoveryStore.RECOVERY_INTENT, RecoveryStore.getInstance( ).getIntent( ));
		if (!RecoveryStore.getInstance( ).getIntents( ).isEmpty( ))
			intent.putParcelableArrayListExtra(RecoveryStore.RECOVERY_INTENTS, RecoveryStore.getInstance( ).getIntents( ));
		intent.putExtra(RecoveryStore.RECOVERY_STACK, Recovery.getInstance( ).isRecoverStack( ));
		intent.putExtra(RecoveryStore.IS_DEBUG, Recovery.getInstance( ).isDebug( ));
		if (mExceptionData != null)
			intent.putExtra(RecoveryStore.EXCEPTION_DATA, mExceptionData);
		if (mStackTrace != null)
			intent.putExtra(RecoveryStore.STACK_TRACE, mStackTrace);
		if (mCause != null)
			intent.putExtra(RecoveryStore.EXCEPTION_CAUSE, mCause);
		Recovery.getInstance( ).getContext( ).startActivity(intent);
		killProcess( );
	}

	private void startRecoverService() {
		Intent intent = new Intent( );
		intent.setClass(Recovery.getInstance( ).getContext( ), RecoveryService.class);
		if (RecoveryStore.getInstance( ).getIntent( ) != null)
			intent.putExtra(RecoveryStore.RECOVERY_INTENT, RecoveryStore.getInstance( ).getIntent( ));
		if (!RecoveryStore.getInstance( ).getIntents( ).isEmpty( ))
			intent.putParcelableArrayListExtra(RecoveryStore.RECOVERY_INTENTS, RecoveryStore.getInstance( ).getIntents( ));
		intent.putExtra(RecoveryService.RECOVERY_SILENT_MODE_VALUE, Recovery.getInstance( ).getSilentMode( ).getValue( ));
		RecoveryService.start(Recovery.getInstance( ).getContext( ), intent);
		killProcess( );
	}

	void register() {
		Thread.setDefaultUncaughtExceptionHandler(this);
	}

	private void killProcess() {
		android.os.Process.killProcess(android.os.Process.myPid( ));
		System.exit(10);
	}

}
